package hms.UserAccountsModule;

import hms.database.DAOnoGUI;
import hms.database.DBRowList;
import hms.database.DBRowObject;
import hms.employees.Employee;
import hms.employees.EmployeeControl;
import hms.employees.JobTitleList;
import hms.employees.TerminationReason;
import hms.employees.TerminationReasonList;
import hms.permissions.*;

public class BenTestCases {
	// Print database table
	public static <ListElement extends DBRowObject> void printTable(String title, DBRowList<ListElement> list) {
		System.out.println(title);
		if(list.isEmpty()) {
			System.out.println("List is empty");	
		} else {
			for(ListElement element : list) {
				System.out.println(element.getUniqueIdentifier()+": "+element);
			}
			System.out.println();
		}
	}

	/**
	 * Remove all of the elements in a DBRowList from the DB.
	 * 
	 * @param list
	 */
	public static <ListElement extends DBRowObject> void cleanTable(DBRowList<ListElement> list) {
		for(ListElement element : list) {
			element.markToBeDeleted();
			element.commitToDatabase();
		}
	}

	/**
	 * Print the current state of the UserAccount/Employee related tables.
	 */
	public static void checkTables() {
		System.out.println("[CURRENT TABLE STATE]");
		UserAccountControl uaList = new UserAccountControl();
		printTable("User Accounts", uaList);
		EmployeeControl emList = new EmployeeControl();
		printTable("Employees",emList);
		TerminationReasonList trList = new TerminationReasonList();
		trList.fillFromDatabase();
		printTable("Termination Reasons",trList);
		JobTitleList jtList = new JobTitleList();
		jtList.fillFromDatabase();
		printTable("Job Titles",jtList);
	}

	// Database test
	public static void main(String[] args) {
		// Open database
		DAOnoGUI.getDAO();
		// Preliminary: Clean the DB tables
		{
			System.out.println("Cleaning tables...\n");


			UserAccountControl uaList = new UserAccountControl();
			cleanTable(uaList);

			EmployeeControl emList = new EmployeeControl();
			cleanTable(emList);

			TerminationReasonList trList = new TerminationReasonList();
			trList.fillFromDatabase();
			cleanTable(trList);

			JobTitleList jtList = new JobTitleList();
			jtList.fillFromDatabase();
			cleanTable(jtList);
		}
		

		boolean test;
		boolean test2;

		// Test read/write
		Employee ben = new Employee();
		EmployeeControl emc = new EmployeeControl();
		emc.add(ben);
		emc.commitToDatabase();
		test = emc.size()>0;
		System.out.println("[List_CommitToDB_Write] Read/write object to DB: " + 
				(test?"OK":"FAIL")
				);
		test2 = test;
		
		// Test delete & empty cases
		emc.get(0).markToBeDeleted();
		emc.commitToDatabase();
		test = emc.size() == 0;
		emc.reloadFromDatabase();
		test = test && emc.size() == 0;
		System.out.println("[List_CommitToDB_Delete] and\n[ObjectCommitToDB_Delete] Object marked as deleted was deleted: " + 
				(test?"OK":"FAIL")
				);
		test2 = test2 && test;
		System.out.println("[List_FillInDB_No] and\n[List_FillInDB_Yes] Load successful with empty and non-empty DB: " + 
				(test?"OK":"FAIL")
				);
		emc.commitToDatabase();
		System.out.println("[List_CommitToDB_No_input] No-input commit: OK");
		
		// Test creation of admin account in empty database
		UserAccountControl adminTest = new UserAccountControl();
		test = adminTest.size() == 1 && adminTest.get(0).getUserName().equals("admin");
		System.out.println("[newUAC_empty] Admin created for empty DB: " + 
				(test?"OK":"FAIL")
				);
		
		// Remove admin account and associated employee account
		adminTest.get(0).markToBeDeleted();
		adminTest.commitToDatabase();
		emc.reloadFromDatabase();
		emc.get(0).markToBeDeleted();
		emc.commitToDatabase();
		
		// Create account benAccount
		UserAccount benAccount = new UserAccount();

		benAccount.setUsername("badcock");
		test = benAccount.getUserName().equals("badcock");
		System.out.println("Set/get username on UserAccount: " + 
				(test?"OK":"FAIL")
				);

		benAccount.setPassword("bananas");
		test = benAccount.checkPassword("bananas");
		System.out.println("Set/check password on UserAccount: " +
				(test?"OK":"FAIL")
				);


		// Make sure Ben is being updated and not re-entered into DB
		benAccount.commitToDatabase();
		benAccount.commitToDatabase();
		adminTest.reloadFromDatabase();
		test = adminTest.size() == 1;
		System.out.println("[ObjectCommitToDB_Update] Object UPDATEd when preexisting in DB: " + 
				(test?"OK":"FAIL")
				);
		
		// Ensure admin is not automatically recreated in non-empty DB
		adminTest = new UserAccountControl();
		test = adminTest.size() == 1 && !adminTest.get(0).getUserName().equals("admin");
		System.out.println("[newUAC_nonempty] Admin NOT created for non-empty DB: " + 
				(test?"OK":"FAIL")
				);
		
		// Test for insert and anti-duplication (TerminationReasons are forbidden to duplicate)
		TerminationReasonList trList = new TerminationReasonList();
		trList.fillFromDatabase();
		int reasons = trList.size();
		trList.add(new TerminationReason("Busy"));
		trList.commitToDatabase();
		trList.reloadFromDatabase();
		test = (trList.size() == (reasons+1));
		System.out.println("[ObjectCommitToDB_Insert] Objects are inserted if they do not previously exist: " + 
				(test?"OK":"FAIL")
				);
		trList.add(new TerminationReason("Busy"));
		trList.commitToDatabase();
		trList.reloadFromDatabase();
		test = (trList.size() == (reasons+1));
		System.out.println("[ObjectCommitToDB_NoDuplicate] Objects do NOT duplicate when forbidden: " + 
				(test?"OK":"FAIL")
				);

		// Test for pro-duplication (Employees are allowed to duplicate)
		emc.reloadFromDatabase();
		int employees = emc.size();
		Employee x = new Employee();
		x.setFirstName("Test");
		emc.add(x);
		Employee y = new Employee();
		y.setFirstName("Test");
		emc.add(y);
		emc.commitToDatabase();
		emc.reloadFromDatabase();
		test = emc.size() == employees+2;
		System.out.println("[ObjectCommitToDB_Duplicate] Objects DO duplicate when permitted: " + 
				(test?"OK":"FAIL")
				);
		// Clear duplicated employees out
		emc.get(employees).markToBeDeleted();emc.get(employees+1).markToBeDeleted();emc.commitToDatabase();

		// Create a new employee, fill out and verify fields
		ben = new Employee();
		test = ben.getFirstName().equals("?");
		ben.setFirstName("Ben");
		ben.setLastName("Adcock");
		test = test && ben.getFullName().equals("Ben Adcock");
		System.out.println("Set first last / get fill name: " +
				(test?"OK":"FAIL")
				);
		
		ben.setPosition("Head Honcho");
		test = ben.getPosition().equals("Head Honcho");
		System.out.println("Set/get Job Title: " +
				(test?"OK":"FAIL")
				);
		
		ben.setHireDate(2012, 04, 01);
		ben.setTerminationDate(2012, 04, 02);
		test = ben.getFireYear() == 2012 &&
				ben.getFireMonth() == 4 &&
				ben.getFireDay() == 2 &&
				ben.getHireDay() == 1 &&
				ben.getHireMonth() == 4 &&
				ben.getHireYear() == 2012;
		System.out.println("Set/get hire and fire dates: " +
				(test?"OK":"FAIL")
				);
		
		
		// Ensure getUniqueIdentifier works properly
		test = ben.getUniqueIdentifier()==-1;
		System.out.println("[ObjectGetUnique_No] Not in DB, -1 UID: " +
				(test?"OK":"FAIL")
				);
		
		ben.commitToDatabase();
		test = ben.getUniqueIdentifier()>-1;
		System.out.println("[ObjectGetUnique_yes] In DB, >-1 UID: " +
				(test?"OK":"FAIL")
				);
		
		// Test linking of separate UserAccounts and Employees
		test = benAccount.getEmployee().isDefault();
		benAccount.linkToEmployee(ben);
		test = test && !benAccount.getEmployee().isDefault();
		System.out.println("Linking employee to user account: " +
				(test?"OK":"FAIL")
				);
		test = benAccount.getFullEmployeeName().equals("Ben Adcock");
		System.out.println("Linking employee to CORRECT user account: " +
				(test?"OK":"FAIL")
				);
		emc.reloadFromDatabase();
		test = emc.size()==1 && emc.get(0).getFullName().equals("Ben Adcock");
		System.out.println("[LinkToEmployee_A] Default employee was deleted: " +
				(test?"OK":"FAIL")
				);
		
		// Add more test data
		UserAccount akilahAccount = new UserAccount();
		Employee akilah = new Employee();
		akilah.setFirstName("Akilah");
		akilah.setLastName("McIntyre");
		akilah.setPosition("CEO");
		akilahAccount.getPermissions().grantPermission(Permission.FULL_ACCESS);
		akilahAccount.setUsername("akilah");
		akilahAccount.setPassword("java");
		akilahAccount.getEmployee().setFirstName("Validated");
		emc.reloadFromDatabase();
		test = emc.size() == 1;
		akilahAccount.linkToEmployee(akilah);
		emc.reloadFromDatabase();
		test = test && emc.size() == 3;
		System.out.println("[LinkToEmployee_B] Modified employee was NOT deleted: " +
				(test?"OK":"FAIL")
				);

		UserAccount mattAccount = new UserAccount("mjohnson","dragonballz");
		mattAccount.getPermissions().grantPermission(Permission.RESERVE_ROOMS);
		mattAccount.getPermissions().grantPermission(Permission.CHECK_IN_OUT);
		Employee matt = mattAccount.getEmployee();
		matt.setFullname("Matt", "Johnson");
		matt.setTerminationReason("Excessive partying");
		matt.setHireDate(2012, 2, 10);
		matt.setTerminationDate(2012, 4, 4);
		mattAccount.commitToDatabase();
		
		
		// Test permission setting
		test = mattAccount.isPermissionGranted(Permission.RESERVE_ROOMS) &&
				!mattAccount.isPermissionGranted(Permission.FULL_ACCESS);
		System.out.println("Get/set permission: " +
				(test?"OK":"FAIL")
				);
		test = matt.getTerminationReason().equals("Excessive partying");
		System.out.println("Get/set termination reason: " +
				(test?"OK":"FAIL")
				);

		// Test termination string- should be Employed for any not-fired employee
		test = akilah.getTerminationReason().equals("Employed");
		System.out.println("Termination reason is \"Employed\" for non-fired employee: " +
				(test?"OK":"FAIL")
				);
		
		{
			// Test getByusername
			UserAccountControl uac = new UserAccountControl();
			UserAccount bena = uac.getByUsername("badcock");
			UserAccount nullAndVoid = uac.getByUsername("fdskljflfkl");
			test = bena!=null && bena.getUserName().equals("badcock");
			System.out.println("[UACgetByUsername_found] Can find username with getUsername: " +
					(test?"OK":"FAIL")
					);
			test = nullAndVoid==null;
			System.out.println("[UACgetByUsername_notfound] Returns null for invalid username: " +
					(test?"OK":"FAIL")
					);
			
			
			// White Box - try checkPassword, then authenticate user

			System.out.println("\n***Thorough password test***");
			// Test checkPassword with good password
			test = bena.checkPassword("bananas");
			System.out.println(test?"Valid password case OK":"Valid password case BAD");

			// Test checkPassword with bad password
			test = bena.checkPassword("badpass");
			System.out.println(test?"Invalid password case BAD":"Invalid password case OK");

			// Test authenticateUser with good password
			test = uac.authenticateUser("badcock", "bananas")!=null;
			System.out.println(test?"Valid password from UAC OK":"Valid password from UAC BAD");

			// Test authenticateUser with bad password
			test = uac.authenticateUser("badcock", "badpassword")!=null;
			System.out.println(test?"Invalid password from UAC BAD":"Invalid password from UAC OK");
			;		}
		
		System.out.println();
		checkTables();
		
		DAOnoGUI.closeDAO();




	}
}
